/**
   Various  Utilities 
   @author J.J.G.
*/

package fr.emse.tatiana.display;

import java.io.File;
import java.util.*;
import org.w3c.dom.*;
import java.awt.Color;

public class Ut {

	/**
	 * A constructor for an element of class Ut
	 * Since this is a utility class, defined only to provide 
	 * a set of procedures
	 *
	 */
    public Ut ()
    {
        System.err.println("Ut constructor called");
    }

    /**
     * Convert its parameter to a boolean if not already one,
     * by interpretating it as the string "yes" "true" "false" "no"
     * or as a number (0 if false, others are true)
     * @param x an object
     * @return a boolean
     */
    public static Boolean booleanize(Object x)
    {
    	Boolean b = true;
    	if (x == null)
    		b = false;
    	else
    	if (x instanceof Boolean)
    		b = (Boolean)x;
    	else
    	if (x instanceof String)
    	{
    		String s = (String)x;
    		if (s.equals("false") || (s.equals("no")))
        		b = false;
    	}
    	else
    	if (x instanceof Integer)
    	{
    		long i = ((Integer)x).intValue();
    		if (i == 0)
        		b = false;
    	}
    	else
    	if (x instanceof Long)
    	{
    		long i = ((Long)x).longValue();
    		if (i == 0)
        		b = false;
    	}
    	return b;
    }
    

    public static Integer set(Integer x, int flg, boolean b)
    {
    	int l = x.intValue();
    	l = (l & (~ flg)) | (b ? flg : 0);
        return new Integer(l);
    }
    
    public static Boolean check(Integer x, int flg)
    {
    	int l = x.intValue();
    	return (l & flg) != 0;
    }
    
    public static String fmtDate(String da)
    {
        Long ii = new Long(da.trim());
        Date dt = new Date(ii.longValue());
        return dt.toString();
    }

    /**
     * This operation returns the "most interesting" element
     * inside an element given as parameter
     * @param no
     * @return the "interesting" node
     */
    public static Node getMain(Node no)
    {
        int ty = 0;
        Node in, txt;
        in = txt = no;
        // On va tenter de trouver "intelligent"
        if (no == null || ! (no instanceof Element))
            return no;
        // Le noeud est un element
        // Get the content
        in = no.getFirstChild();
        if (in == null)
            return in;
        while (in != null)
        {
            ty = in.getNodeType();
            if (ty == org.w3c.dom.Node.ELEMENT_NODE)
            {
                // We are happy with the first element
                return in;
            }
            if (ty == org.w3c.dom.Node.TEXT_NODE)
            {
                txt = in;
            }
            in = in.getNextSibling();
        } 
       return txt;
    }

    public static String fmtNode(Node no)
    {
        int algo = 0;
        Node in, txt, tim ;
        in = txt = tim = no;
        // On va tenter de formatter "intelligent"
        // Est-ce in noeud "info" ?
        if (no == null)
            return "";
        if (no.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE && no.getNodeName() == "info")
        {
            // Get the content
            in = no.getFirstChild();
            while (in != null)
            {
                if (in.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE)
                {
                    String nt = in.getNodeName();
                    if (nt.equals("time"))
                    {
                        tim = in ; algo = 1;
                    }
                    if (nt.equals("text"))
                    {
                        txt = in ; algo = 2;
                    }
                }
                in = in.getNextSibling();
            }
        } 
        switch (algo)
        {
        case 1 :
           // Is this a date ?
           // Need the contents of first elem "date"
           in = tim.getFirstChild();
           while (in != null)
           {
               if (in.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE
                  && in.getNodeName().equals("date"))
               {
                   return Ut.fmtDate(in.getTextContent());
               }
               in = in.getNextSibling();
           }
           break;
       case 2 :
           no = txt;
       }
       return no.getTextContent().trim();
    }

    /*
        Get a node by name inside an other node
    */
    public static Node getNode(Node src, String idt)
    {
        Node in = null;
        if (src == null)
             return src;
        if (src.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE)
        {
             in = src.getFirstChild();
             while (in != null)
             {
                 if (in.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE
                   && in.getNodeName().equals(idt))
                       return in;
                 in = in.getNextSibling();
             }
        }
        return in;
    }

    public static Node getNode(Node src, String idt1, String idt2)
    {
        return getNode(getNode(src, idt1), idt2);
    }

    public static Node getNode(Node src, String idt1, String idt2, String idt3)
    {
        return getNode(getNode(getNode(src, idt1), idt2), idt3);
    }

    public static String getValue(Node src)
    {
        if (src == null)
            return "";
        return src.getTextContent();
    }

    public static String getValue(Node src, String idt1)
    {
        return getValue(getNode(src, idt1));
    }

    public static String getValue(Node src, String idt1, String idt2)
    {
        return getValue(getNode(getNode(src, idt1), idt2));
    }

    public static Node getInfo(Node src, String idt)
    {
        Node in = null;
        if (src == null)
             return src;
        if (src.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE)
        {
             in = src.getFirstChild();
             while (in != null)
             {
                 if (in.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE
                   && in.getNodeName().equals("info"))
                 {
                      Element elt = (Element)in;
                      String atn = elt.getAttribute("name");
                      if (atn.equals(idt))
                      {
                           return in;
                      }
                 }
                 in = in.getNextSibling();
             }
        }
        return src;
    }

    
    public static DisplayDate datize(Object x)
    {
    	long date, duration;
    	date = duration = 0;
    	boolean found = false;
    	if (x == null)
    		return new DisplayDate();
    	if (x instanceof String)
    	{
            Long ii;
            try {
      	        ii = new Long((String)x);
            } catch (NumberFormatException e)
            {
            	  ii = new Long(0);
            }
            date = ii.longValue();
    	}
    	else
    	if (x instanceof Node)
    	{
    		Node src = (Node)x;
            if ((src.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) &&
            		src.getNodeName().equals("time"))
            {
            	// Let's decode the content
            	Node in = src.getFirstChild();
            	while (in != null)
            	{
            		if (in.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE)
            		{
            			if (in.getNodeName().equals("date"))
            			{
            				Long ii;
            	            try {
            	      	        ii = new Long(in.getTextContent().trim());
            	            } catch (NumberFormatException e)
            	            {
            	            	  ii = new Long(0);
            	            }
            	            if (found)
            	            	duration = ii.longValue() - date;
            	            else
            	            	date = ii.longValue();
            	            found = true;
            			}
            			else
            			if (in.getNodeName().equals("duration"))
            			{
            				Long ii;
            	            try {
            	      	        ii = new Long(in.getTextContent().trim());
            	            } catch (NumberFormatException e)
            	            {
            	            	  ii = new Long(0);
            	            }
            	            duration = ii.longValue();
            			}
            			else
            			{
            				System.err.println("Unknown Element in \"time\" structure " + in.getNodeName());
            			}
            		}
            		in = in.getNextSibling();
            	}
            }
    		
    	}
    	return new DisplayDate(date,duration);
    }
    
    static String[] colours = {
        "Yellow",        "LightYellow",    "LightGreen",      "Black",
        "White",         "Red",            "Green",           "Blue",
        "LightRed",      "LightBlue",      "Gray",            "LightGray",
        "Orange",        "Purple",
        "DarkRed",       "DarkGreen",      "DarkBlue",
    };
    static int[] cvals = {
        255, 255, 0,     255, 255, 191,    191, 255, 191,     0,0,0,
        255,255,255,     255,0,0,          0,255,0,           0,0,255,
        255,191,191,     191,191,255,      127,127,127,       191,191,191,
        255,191,0,       191,0,191,
        191,0,0,         0,191,0,          0,0,191,
    };


    public static int decodeHex(String hs)
    {
         int i = 0;
         try
         {
             i = Integer.parseInt(hs,16);
         }
         catch (NumberFormatException ie)
         {
             i = 0;
         };
         return i;
    }

    static final String hS = "0123456789ABCDEF";
    
    public static String to2hex(int w)
    {
    	int i, j;
    	if (w <0) w = 0;
    	if (w > 255) w = 255;
    	i = w/16;
    	j = w % 16;
    	return hS.substring(i,i+1)+hS.substring(j,j+1);
    }
    public static Color decodeColor(String col)
    {
        int i;
        // System.out.println(" Looking for color "+col);
        if (col == null)
            return Color.black;
        String rc = col.trim().toLowerCase();
        if (rc == "")
            return Color.black;
        if (rc.startsWith("#"))
        {
            return new Color(decodeHex(rc.substring(1,3)),
                 decodeHex(rc.substring(3,5)), decodeHex(rc.substring(5,7)));
        }
        for (i=0; i<colours.length; i++)
        {
             if (colours[i].toLowerCase().equals(rc))
             {
                return new Color(cvals[3*i], cvals[3*i+1], cvals[3*i+2]);
             }
        }
        // Default...
        System.out.println(" Looking for color "+col+"("+rc+")... not found");
        return Color.cyan;
    }

    /**
     * Convert its parameter to a boolean if not already one,
     * by interpretating it as the string "yes" "true" "false" "no"
     * or as a number (0 if false, others are true)
     * @param x an object
     * @return a boolean
     */
    public static Color colorize(Object x)
    {
    	Color c = Color.cyan;
    	if (x == null)
    	{
    		c = Color.cyan;
    	}
    	else
    	if (x instanceof Color)
    	{
    		c = (Color)x;
    	}
    	else
    	if (x instanceof String)
    	{
    		c = decodeColor((String)x);
    	}
    	else
    	{
    		c = Color.cyan;
    	}
    	return c;
    }
    
    /**
     * Return ou vision of the name of a color, according to its values
     * @param x the color to characterize...
     * @return
     */
    public static String colorName(Color x)
    {
    	int cr, cg, cb;
    	cr = x.getRed();
    	cg = x.getGreen();
    	cb = x.getBlue();
        int i;

        for (i=0; i<colours.length; i++)
        {
             if ((cr == cvals[3*i]) && (cg == cvals[3*i+1]) && (cb == cvals[3*i+2]))
             {
                return colours[i];
             }
        }
        
    	return "#"+to2hex(cr)+to2hex(cg)+to2hex(cb);
    }
    
    private static void buildBrothers(StringBuffer sb, Node nd)
    {
         while (nd != null)
         {
             buildNode(sb, nd);
             nd = nd.getNextSibling();
         }
    }

    private static void buildAttribute(StringBuffer sb, Node nd)
    {
        sb.append(" ");
        sb.append(nd.getNodeName());
        sb.append("=\"");
        sb.append(nd.getNodeValue());
        sb.append("\"");
    }

    private static void buildNode(StringBuffer sb, Node nd)
    {
            String id;
            int i;
            NamedNodeMap nm;
            if (nd == null)
                return;
            switch (nd.getNodeType())
            {
            case org.w3c.dom.Node.DOCUMENT_NODE :
                  buildBrothers(sb, nd.getFirstChild());
                  break;
            case org.w3c.dom.Node.ELEMENT_NODE :
                  id = nd.getNodeName();
                  sb.append("<"); sb.append(id);
                  nm = nd.getAttributes();
                  if (nm != null && nm.getLength() != 0)
                  {
                      for (i=0; i<nm.getLength(); i++)
                      {
                           buildAttribute(sb,nm.item(i));
                      }
                  }   
                  sb.append(">");
                  buildBrothers(sb, nd.getFirstChild());
                  sb.append("</"); sb.append(id); sb.append(">");
                  break;
            case org.w3c.dom.Node.TEXT_NODE :
                  id = nd.getTextContent();
                  if (!id.trim().equals(""))
                      sb.append(id);
                  break;
            default :
                  sb.append("{{"+nd.getNodeType()+"}}");
            }
    }

    /**
     * Return as a "serialized XML" string the content of a Node
     * This is a very cheap implementation...
     * @param nd : a dom Node 
     * @return a string
     */
    public static String formatXMLNode(Node nd)
    {
         StringBuffer sb = new StringBuffer();
         buildNode(sb, nd);
         return sb.toString();
    }
    
    /**
     * Retuns as a "serialized XML" string the content of a Display item
     * For now, a display item just encapsulate a Node...
     * @param di : a display item
     * @return : a string
     */
    public static String formatXMLNode(DisplayItem di)
    {
    	return formatXMLNode(di.getElement());
    }

    private static final int [] chEsc = {34, 38, 39, 60, 62 };
    private static final String [] rpEsc = {"&quot;","&amp;", "&apos;", "&lt;", "&gt;" };
    /**
     * Convert a String to an XML escaped string by handling the
     * 5 famous chars, <, >, ", ' and &.
     * @param s : the string to convert
     * @return the converted string, or the original if nothing to convert
     */
    public static String toXML(String s)
    {
    	boolean ok = true;
    	int i,j, c;
    	for (i=0; i<chEsc.length; i++)
    	{
    		if (s.indexOf(chEsc[i]) >= 0)
    			ok = false;
    	}
    	if (ok)
    	    return s;
    	StringBuffer sb = new StringBuffer();
        for (j=0; j <s.length(); j++)
        {
        	c = s.codePointAt(j);
        	ok = true;
        	for (i=0; i< chEsc.length && ok; i++)
        	{
        		if (c == chEsc[i])
        		{
        			ok = false; sb.append(rpEsc[i]);
        		}
        	}
        	if (ok)
        	{
        		sb.append((char)c);
        	}
        }
    	return sb.toString();
    }
    
    private static String[] mT = {
    	"icon", "img", "icon",
    	"bgcolor", "color", "bgcolor",
    	"string", "text", "text",
    	"#text", "text", "text"
    };
    
    public static String mergeTypes(String t1, String t2)
    {
    	if ((t1 == null) || (t1.equals("")))
    		return t2;
    	if ((t2 == null) || (t2.equals("")))
    		return t1;
    	if (t1.equals(t2))
    		return t1;
    	if (t1.equals("any")|| t2.equals("any"))
    		return "any";
    	if (t1.compareTo(t2) > 0)
    	{
    		String t = t1; t1 = t2; t2 = t;
    	}
    	for (int i=0; i<mT.length; i=i+3)
    		if (t1.equals(mT[i]) && t2.equals(mT[i+1]))
    			return mT[i+2];
    	return "any";
    }


    private static String[] cT = {
    	"icon", "img", "icon",
    	"bgcolor", "color", "bgcolor",
    	"string", "text", "text",
    	"#text", "text", "text"
    };
    
    public static String convertPossible(String t1, String t2)
    {
    	if ((t1 == null) || (t1.equals("")))
    		return t2;
    	if ((t2 == null) || (t2.equals("")))
    		return t1;
    	if (t1.equals(t2))
    		return t1;
    	if (t1.compareTo(t2) > 0)
    	{
    		String t = t1; t1 = t2; t2 = t;
    	}
    	for (int i=0; i<mT.length; i=i+3)
    		if (t1.equals(mT[i]) && t2.equals(mT[i+1]))
    			return mT[i+2];
    	return "";
    }
    /**
     * Compute the Tatiana vision of a Java data type
     * @param val : the Java value
     * @return : a String that represents the Tatiana Name of the 
     * object type
     */
    public static String getTType(Object val)
    {
    	String str;
    	if (val == null)
    	{
    		str = "";
    	}
    	if (val instanceof String)
    	{
            str = "text";
    	}
    	else
    	if (val instanceof Color)
    	{
            str = "color";
    	}
    	else
    	if (val instanceof DisplayCell)
    	{
            str = "cell";
    	}
    	else
    	if (val instanceof Integer)
    	{
            str = "int";
    	}
    	else
    	if (val instanceof Long)
    	{
            str = "long";
    	}
    	else
    	if (val instanceof Boolean)
    	{
            str = "boolean";
    	}
    	else
    	if (val instanceof DisplayDate)
    	{
            str = "time";
    	}
    	else
    	if (val instanceof org.w3c.dom.Node)
    	{
            str = ((org.w3c.dom.Node) val).getNodeName();
    	}
    	else
    	if (val instanceof DisplayIcon)
    	{
            str = "icon";
    	}
    	else
    	if (val instanceof File)
    	{
            str = "file";
    	}
    	else
    	if (val instanceof Date)
    	{
            str = "time";
    	}
    	else
    	{
    		str = "#" + Ut.toXML(val.getClass().getName()) + "#";
    	}
    	return str;
    }
    
    public static org.eclipse.swt.graphics.RGB toSWTRGB (Color c) {
    	return new org.eclipse.swt.graphics.RGB(
    			c.getRed(),
    			c.getGreen(),
    			c.getBlue()
    	);
    }
    
    public static Color toAWTColor (org.eclipse.swt.graphics.RGB c) {
    	return new Color(
    			c.red,
    			c.green,
    			c.blue
    	);
    }
    
}

